#include "CFG.h"

CFG::CFG(StmtTable *st)
{
	myST = st;

};

Schema CFG::getSynSyn(DESIGN_ENTITY de1, NAME de1Name, DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	attNames.push_back(de2Name);
	Schema x(2,attNames);
	if(de1Name!=de2Name)
	{
		for (auto it = forward.begin(); it != forward.end(); ++it) {
			if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && it->second != -1 && (myST->stmtTable.at(it->second) == de2 || de2 == "stmt")) {
				vector<INDEX> vect;
				vect.push_back(it->first);
				vect.push_back(it->second);
				x.insertTuple(vect);
			}
		}
		return x;
	}
	else
	{
		return x;
	}
};

Schema CFG::getSynInt(DESIGN_ENTITY de1, NAME de1Name, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt") && it->second==para2) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getSynUnd(DESIGN_ENTITY de1, NAME de1Name)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if ((myST->stmtTable.at(it->first) == de1 || de1 == "stmt")) {
			vector<INDEX> vect;
			vect.push_back(it->first);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getIntSyn(INDEX para1, DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if (it->first==para1 && it->second != -1 && (myST->stmtTable.at(it->second) == de2 || de2 == "stmt")) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getIntInt(INDEX para1, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if ((it->first==para1) && it->second != -1 && (it->second==para2)) {
			vector<INDEX> vect;
			vect.push_back(para1);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getIntUnd(INDEX para1)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if (it->first==para1) {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getUndInt(INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if (it->second != -1 && (it->second==para2)) {
			vector<INDEX> vect;
			vect.push_back(para2);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getUndSyn(DESIGN_ENTITY de2, NAME de2Name)
{
	vector<NAME> attNames;
	attNames.push_back(de2Name);
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		if (it->second != -1 && myST->stmtTable.at(it->second) == de2 || de2 == "stmt") {
			vector<INDEX> vect;
			vect.push_back(it->second);
			x.insertTuple(vect);
		}
	}
	return x;
};

Schema CFG::getUndUnd()
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);
	for (auto it = forward.begin(); it != forward.end(); ++it) 
	{
		vector<INDEX> vect;
		vect.push_back(it->second);
		x.insertTuple(vect);
	}
	return x;
};

void CFG::refineNext()
{
	INDEX lastSeen = -1;
	vector<set<INDEX>> pre;
	vector<INDEX> aaa;
	for (auto it = forward.begin(); it != forward.end(); ++it) 
	{
		if (myST->stmtTable.at(it->first)=="if" && it->second!=-1) 
		{
			if (lastSeen == it->first) 
			{
				pre.at(pre.size()-1).insert(it->second); 
			} 
			else 
			{
				lastSeen = it->first;
				set<INDEX> newSet;
				newSet.insert(it->second);
				pre.push_back(newSet);
				aaa.push_back(it->first);
			}	
		}
	}
	unordered_multimap<INDEX,INDEX> HCF;
	unordered_set<INDEX> noNeedToChange;
	
	for (int i = 0; i < pre.size(); ++i) 
	{
		int localH = -1;
		for (auto it2 = pre.at(i).begin(); it2 != pre.at(i).end(); ++it2) 
		{
			if(*it2 > localH)
			{
				localH = *it2;
			}
			if ( (pre.at(i).size()==2) || (*it2<aaa.at(i) && pre.at(i).size()>2) )
			{
				noNeedToChange.insert(aaa.at(i));
			}
		}
		HCF.insert(std::make_pair<INDEX,INDEX>(aaa.at(i),localH));
	}
	//need to work here....
	for (auto it = forward.begin(); it != forward.end(); ++it) 
	{
		if (it->second!=-1 && myST->stmtTable.at(it->second)=="if" && it->second<it->first) 
		{
			unordered_set<INDEX>::const_iterator got = noNeedToChange.find(it->second);	
			if (got == noNeedToChange.end()) {
				INDEX nowToPointTo = -1;
				for (auto it2 = HCF.begin(); it2 != HCF.end(); ++it2) 
				{
					if (it->second==it2->first) 
					{
						nowToPointTo = it2->second;
						break;
					}
				}
				it->second = nowToPointTo;
			} 
			else 
			{
				bool lololo = false;
				for (auto it2 = forward.begin(); it2 != forward.end(); ++it2) 
				{
					if((it2->first==it->second) && (it2->second < it2->first))
					{
						it->second = it2->second;
						lololo = true;
						break;
					}
				}
				if(!lololo)
				{
					it->second = -1;
				}
			}
		}
	}
	for (auto it = forward.begin(); it != forward.end(); ++it) 
	{
		if (myST->stmtTable.at(it->first)=="if" && it->second != -1)
		{
			unordered_set<INDEX>::const_iterator got = noNeedToChange.find(it->first);	
			if (got == noNeedToChange.end()) {
				INDEX larger;
					for (auto it2 = HCF.begin(); it2 != HCF.end(); ++it2) 
					{
						if (it->first==it2->first) 
						{
							larger = it2->second;
							break;
						}
					}
					if(it->second==larger)
					{
						it->second = -1;
					}
				} 
				else 
				{
				
				}
		}
	}
};

Schema CFG::getSynIntClosure(DESIGN_ENTITY de1, NAME de1Name, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back(de1Name);
	Schema x(1,attNames);

	//-----------------------------------------------------------------------

	unordered_set<INDEX> accum;
	bool isChanged;
	int initSize;
	
	isChanged = true;
	accum.clear();

	for (auto it = forward.begin(); it != forward.end(); ++it)
	{
		if(it->second == para2)
		{
			accum.insert(it->first);
		}
	}

	while(isChanged)
	{
		isChanged = false;
		initSize = accum.size();
			
		for (auto it = accum.begin(); it != accum.end(); ++it)
		{
			for (auto it2 = forward.begin(); it2 != forward.end(); ++it2)
			{
				if(*it == it2->second)
				{
					accum.insert(it2->first);
				}
			}
		}

		if(initSize!=accum.size())
		{
			isChanged = true;
		}
	}

	for (auto it = accum.begin(); it != accum.end(); ++it)
	{
		vector<INDEX> vect;
		vect.push_back(*it);
		
		x.insertTuple(vect);
	}
	return x;
};

Schema CFG::getIntIntClosure(INDEX para1, INDEX para2)
{
	vector<NAME> attNames;
	attNames.push_back("_");
	Schema x(1,attNames);

	//-----------------------------------------------------------------------

	unordered_set<INDEX> accum;
	bool isChanged;
	int initSize;
	
	isChanged = true;
	accum.clear();

	for (auto it = forward.begin(); it != forward.end(); ++it)
	{
		if(it->first == para1)
		{
			accum.insert(it->second);
		}
	}

	while(isChanged)
	{
		isChanged = false;
		initSize = accum.size();
			
		for (auto it = accum.begin(); it != accum.end(); ++it)
		{
			for (auto it2 = forward.begin(); it2 != forward.end(); ++it2)
			{
				if(*it == it2->first)
				{
					accum.insert(it2->second);
				}
			}
		}

		if(initSize!=accum.size())
		{
			isChanged = true;
		}
	}

	for (auto it = accum.begin(); it != accum.end(); ++it)
	{
		if(*it==para2)
		{
			vector<INDEX> vect;
			vect.push_back(*it);	
			x.insertTuple(vect);
		}
	}

	return x;
};

void CFG::printAll() {
	cout << "Printing Parent Table" << endl;
	for (auto it = forward.begin(); it != forward.end(); ++it) {
		cout << it->first << " : " << it->second << endl;
	}
};